package MyThread_demo.zidingyixianchengchi;

import lombok.extern.slf4j.Slf4j;

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author ChenYongHao
 * @date 2023/5/4 15:50
 * @desc
 */
// 阻塞队列
@Slf4j
public class BlockingQueue<T> {
    private Deque<T> deque = new ArrayDeque<>();            // 用来存储阻塞的任务
    private ReentrantLock lock = new ReentrantLock();       // 用来对阻塞队列加锁
    private Condition fullWaitSet = lock.newCondition();    // 生产者条件变量
    private Condition emptyWaitSet = lock.newCondition();   // 消费者条件变量
    private int capcity;                                    // 阻塞队列的容量

    // 无参构造函数
    public BlockingQueue() {
        this.capcity = 4;
    }

    // 有参构造函数
    public BlockingQueue(int capcity) {
        this.capcity = capcity;
    }

    // 获取队列大小
    public int size() {
        lock.lock();
        try {
            return deque.size();
        } finally {
            lock.unlock();
        }
    }

    /**
     * 向阻塞队列中添加任务：
     *
     * 如果容量已经满了，那么该任务要阻塞
     * 如果容量还有剩余，那么该任务要入队
     */
    // 向阻塞队列中添加任务
    public boolean put(T task) {
        lock.lock();
        try {
            // 如果容量已经满了，那么该任务要阻塞
            while (deque.size() == capcity) {
                try {
                    log.info("等待加入阻塞队列：{}", task);
                    fullWaitSet.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            // 如果容量还有剩余，那么该任务要入队
            log.info("已经加入阻塞队列：{}", task);
            deque.addLast(task);
            emptyWaitSet.signal();
            return true;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 向阻塞队列中获取任务：
     *
     * 如果容量已经空了，那么该请求要阻塞
     * 如果容量还有剩余，那么直接返回任务
     */

    // 向阻塞队列中获取任务
    public T take() {
        lock.lock();
        try {
            // 如果容量已经空了，那么该请求要阻塞
            while (deque.isEmpty()) {
                try {
                    emptyWaitSet.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            // 如果容量还有剩余，那么直接返回任务
            T task = deque.removeFirst();
            fullWaitSet.signal();
            return task;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 向阻塞队列中添加任务（带超时时间）：
     *
     * 如果容量已经满了，那么该任务要阻塞
     * 如果容量还有剩余，那么该任务要入队
     */

    // 向阻塞队列中添加任务（带超时时间）
    public boolean offer(T task, Long timeout, TimeUnit timeUnit) {
        lock.lock();
        try {
            long nanos = timeUnit.toNanos(timeout);
            // 如果容量已经满了，那么该任务要阻塞
            while (deque.size() == capcity) {
                try {
                    log.info("等待加入阻塞队列：{}", task);
                    // awaitNanos的返回值是剩余时间，如果<=0，则表示超时
                    if (nanos <= 0) {
                        return false;
                    }
                    nanos = fullWaitSet.awaitNanos(nanos);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            // 如果容量还有剩余，那么该任务要入队
            log.info("已经加入阻塞队列：{}", task);
            deque.addLast(task);
            emptyWaitSet.signal();
            return true;
        } finally {
            lock.unlock();
        }
    }


    /**
     * 向阻塞队列中获取任务（带超时时间）：
     *
     * 如果容量已经空了，那么该请求要阻塞
     * 如果容量还有剩余，那么直接返回任务
     */

    // 向阻塞队列中获取任务（带超时时间）
    public T poll(Long timeout, TimeUnit timeUnit) {
        lock.lock();
        try {
            long nanos = timeUnit.toNanos(timeout);
            // 如果容量已经空了，那么该请求要阻塞
            while (deque.isEmpty()) {
                try {
                    // awaitNanos的返回值是剩余时间，如果<=0，则表示超时
                    if (nanos <= 0) {
                        return null;
                    }
                    nanos = emptyWaitSet.awaitNanos(nanos);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            // 如果容量还有剩余，那么直接返回任务
            T task = deque.removeFirst();
            fullWaitSet.signal();
            return task;
        } finally {
            lock.unlock();
        }
    }


}

